﻿#region imports

using System;
using System.Globalization;
using System.Security.Principal;
using System.Text;
using System.Web;
using Microsoft.Data.Services.Toolkit;

#endregion

namespace Codeplex.ODataTFS.Web.Infrastructure
{
	public sealed class TfsAuthenticationModule : IHttpModule
	{
		private static Uri _tfsServerUri;

		public TfsAuthenticationModule()
		{
			_tfsServerUri = new Uri(ConfigReader.GetConfigValue("ODataTFS.TfsServer"), UriKind.Absolute);
		}

		public void Init(HttpApplication context)
		{
			context.AuthenticateRequest += AuthenticateRequest;
			context.BeginRequest += BeginRequest;
		}

		// Callback
		private void BeginRequest(object sender, EventArgs e)
		{
			var context = sender as HttpApplication;
			if (context != null && context.User == null)
			{
				if (!context.Request.IsMetadataRequest() && !context.Request.IsRootCollectionListRequest(1) && !TryAuthenticate(context))
				{
					SendAuthHeader(context);
				}
			}
		}

		// Callback
		private void AuthenticateRequest(object sender, EventArgs e)
		{
			var context = sender as HttpApplication;

			TryAuthenticate(context);
		}

		private static void SendAuthHeader(HttpApplication context)
		{
			context.Response.Clear();
			context.Response.ContentType = "application/xml";
			context.Response.StatusCode = 401;
			context.Response.StatusDescription = "Unauthorized";
			context.Response.AddHeader("WWW-Authenticate", string.Format(CultureInfo.InvariantCulture, "Basic realm=\"{0}\"", _tfsServerUri.AbsoluteUri));
			context.Response.AddHeader("DataServiceVersion", "1.0");
			context.Response.Write(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes"" ?>
                                     <error xmlns=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"">
                                         <code>Not authorized</code>
                                         <message xml:lang=""en-US"">Please provide valid TFS credentials (domain\\username and password)</message>
                                     </error>");
			context.Response.End();
		}

		private bool TryAuthenticate(HttpApplication context)
		{
			string user = null;
			string password = null;
			string domain = null;

			string authHeader = context.Request.Headers["Authorization"];
			if (!string.IsNullOrEmpty(authHeader))
			{
				if (authHeader.StartsWith("basic ", StringComparison.InvariantCultureIgnoreCase))
				{
					string userNameAndPassword = Encoding.Default.GetString(Convert.FromBase64String(authHeader.Substring(6)));
					string[] userAndPasswordArray = userNameAndPassword.Split(new[] {':'}, 2, StringSplitOptions.RemoveEmptyEntries);
					if (userAndPasswordArray.Length >= 2)
					{
						string[] userAndDomainArray = userAndPasswordArray[0].Trim().Split(new[] {'\\'}, 2, StringSplitOptions.RemoveEmptyEntries);
						password = userAndPasswordArray[1];

						if (userAndDomainArray.Length >= 2)
						{
							domain = userAndDomainArray[0];
							user = userAndDomainArray[1];
						}
						else
						{
							user = userAndDomainArray[0];
						}
					}
					else
					{
						return false;
					}

					TfsUser bu;
					if (IsValidUser(user, password, domain, out bu))
					{
						context.Context.User = new GenericPrincipal(bu, new string[] {});
						if (!IsRequestAllowed(context.Request, bu))
						{
							return false;
						}

						return true;
					}
				}
			}

			return false;
		}

		private bool IsValidUser(string userName, string password, string domain, out TfsUser user)
		{
			if (string.IsNullOrWhiteSpace(userName))
			{
				user = null;
				return false;
			}
			if (string.IsNullOrWhiteSpace(password))
			{
				user = null;
				return false;
			}
			if (string.IsNullOrWhiteSpace(domain))
			{
				user = null;
				return false;
			}

			user = new TfsUser
				{
					Domain = domain,
					UserName = userName,
					Password = password
				};

			return true;
		}

		private bool IsRequestAllowed(HttpRequest request, TfsUser user)
		{
			if (user != null)
			{
				return true;
			}

			return false;
		}

		public void Dispose()
		{
		}
	}
}
